#include "stdafx.h"
#include "Application.h"
#include "Framework.h"

#include "DXUT.h"

#include <cassert>
#include <exception>

#include <rt/Exceptions/COMException.h>

namespace MandelShader
{
	class Application : public IApplication
	{
	public:
		Application(IFramework *framework)
			: m_framework(framework),
			m_shader(0)
		{ }
		virtual ~Application() { }

		virtual void Startup();
		virtual void Shutdown();

		virtual bool IsDeviceAcceptable(CD3D11EnumAdapterInfo const *adapterInfo, UINT output, CD3D11EnumDeviceInfo const *deviceInfo,
			DXGI_FORMAT backBufferFormat, bool windowed);
		virtual bool ModifyDeviceSettings(DXUTDeviceSettings *deviceSettings);
		virtual HRESULT OnCreateDevice(ID3D11Device *device, DXGI_SURFACE_DESC const *backBufferSurfaceDesc);
		virtual HRESULT OnResizedSwapChain(ID3D11Device *device, IDXGISwapChain *swapChain, DXGI_SURFACE_DESC const *backBufferSurfaceDesc);
		virtual void OnFrameMove(double currentTime, float elapsedTime);
		virtual void OnFrameRender(ID3D11Device *device, ID3D11DeviceContext *immediateContext, double currentTime, float elapsedTime);
		virtual void OnReleasingSwapChain();
		virtual void OnDestroyDevice();
		virtual LRESULT MsgProc(HWND window, UINT msg, WPARAM wp, LPARAM lp, bool *noFurtherProcessing);
		virtual void OnKeyboard(UINT keyChar, bool keyDown, bool altDown);
		virtual void OnMouse(bool leftButtonDown, bool rightButtonDown, bool middleButtonDown, bool sideButton1Down, bool sideButton2Down, int mouseWheelDelta, int x, int y);
		virtual bool OnDeviceRemoved();

	private:
		IFramework *m_framework;
		ID3D11ComputeShader *m_shader;
	};

	void Application::Startup()
	{
	}

	void Application::Shutdown()
	{
	}

	bool Application::IsDeviceAcceptable(CD3D11EnumAdapterInfo const *adapterInfo, UINT output, CD3D11EnumDeviceInfo const *deviceInfo,
		DXGI_FORMAT backBufferFormat, bool windowed)
	{
		return deviceInfo->ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x == TRUE;
	}

	bool Application::ModifyDeviceSettings(DXUTDeviceSettings *deviceSettings)
	{
		return true;
	}

	static DWORD ShaderFlags()
	{
		DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
		// Set the D3D10_SHADER_DEBUG flag to embed debug information in the shaders.
		// Setting this flag improves the shader debugging experience, but still allows 
		// the shaders to be optimized and to run exactly the way they will run in 
		// the release configuration of this program.
		shaderFlags |= D3D10_SHADER_DEBUG;
#endif
		return shaderFlags;
	}

	HRESULT Application::OnCreateDevice(ID3D11Device *device, DXGI_SURFACE_DESC const *backBufferSurfaceDesc)
	{
		try
		{
			ID3D10Blob *byteCode = 0;
			ID3D10Blob *errors = 0;
			rt::tstring path(m_framework->FindMedia(_T("ComputeShader.hlsl")));
			HRESULT const hr = m_framework->CompileFromFile(path, ShaderFlags(), "Compute", "cs_4_0", &byteCode, &errors);
			if (FAILED(hr))
			{
				if (errors)
				{
					m_framework->DisplayCompileErrorMessage(static_cast<char const *>(errors->GetBufferPointer()));
					errors->Release();
				}
				THR(hr);
			}
			assert(!errors);

			THR(device->CreateComputeShader(byteCode->GetBufferPointer(), byteCode->GetBufferSize(), 0, &m_shader));

			return S_OK;
		}
		catch (rt::COMException const &bang)
		{
			return bang.Status();
		}
		catch (...)
		{
			return E_FAIL;
		}
	}

	HRESULT Application::OnResizedSwapChain(ID3D11Device *device, IDXGISwapChain *swapChain, DXGI_SURFACE_DESC const *backBufferSurfaceDesc)
	{
		return S_OK;
	}

	void Application::OnFrameMove(double currentTime, float elapsedTime)
	{
	}

	void Application::OnFrameRender(ID3D11Device *device, ID3D11DeviceContext *immediateContext, double currentTime, float elapsedTime)
	{
		// Clear render target and the depth stencil 
		float ClearColor[4] = { 0.176f, 0.196f, 0.667f, 0.0f };

		ID3D11RenderTargetView* pRTV = m_framework->GetRenderTargetView();
		ID3D11DepthStencilView* pDSV = m_framework->GetDepthStencilView();
		immediateContext->ClearRenderTargetView( pRTV, ClearColor );
		immediateContext->ClearDepthStencilView( pDSV, D3D11_CLEAR_DEPTH, 1.0, 0 );
	}

	void Application::OnReleasingSwapChain()
	{
	}

	void Application::OnDestroyDevice()
	{
		m_shader->Release();
		m_shader = 0;
	}

	LRESULT Application::MsgProc(HWND window, UINT msg, WPARAM wp, LPARAM lp, bool *noFurtherProcessing)
	{
		return 0;
	}

	void Application::OnKeyboard(UINT keyChar, bool keyDown, bool altDown)
	{
	}

	void Application::OnMouse(bool leftButtonDown, bool rightButtonDown, bool middleButtonDown, bool sideButton1Down, bool sideButton2Down, int mouseWheelDelta, int x, int y)
	{
	}

	bool Application::OnDeviceRemoved()
	{
		return true;
	}

	IApplication *CreateApplication(IFramework *framework)
	{
		return new Application(framework);
	}
}
